package cn.gyn.gynrpc.core.config;

import cn.gyn.gynrpc.core.api.*;
import cn.gyn.gynrpc.core.cluster.GrayRouter;
import cn.gyn.gynrpc.core.cluster.RoundRibbonLoadBalancer;
import cn.gyn.gynrpc.core.consumer.ConsumerBootstrap;
import cn.gyn.gynrpc.core.filter.ParameterFilter;
import cn.gyn.gynrpc.core.meta.InstanceMeta;
import cn.gyn.gynrpc.core.registry.gyn.GynRegistryCenter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;

import java.util.List;

@Slf4j
@Configuration
@Import({AppConfigProperties.class, ConsumerProperties.class})
public class ConsumerConfig {

    @Autowired
    AppConfigProperties appConfigProperties;

    @Autowired
    ConsumerProperties consumerProperties;

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "apollo.bootstrap", value = "enabled")
    ApolloChangedListener consumer_apolloChangedListener() {
        return new ApolloChangedListener();
    }

    @Bean
    ConsumerBootstrap consumerBootstrap() {
        return new ConsumerBootstrap();
    }

    /**
     * class2 00:22:00
     * 在spring上下文加载完成后，调用此方法填充stub,去给所有注解了GynConsumer的field赋值请求，然后封装http请求，调用provider
     * @return
     */
    //Order 注解提高执行优先级，数越小执行优先级越高
    @Bean
    @Order(Integer.MIN_VALUE + 1)
    public ApplicationRunner consumerBootstrap_runner(@Autowired ConsumerBootstrap consumerBootstrap) {
        return x -> {
            log.info("consumerBootstrap_runner starting");
            consumerBootstrap.start();
            log.info("consumerBootstrap_runner started");
        };
    }

    //装配Bean
    @Bean
    LoadBalancer<InstanceMeta> loadBalancer() {
        //默认其中一个
        //return LoadBalancer.Default;
        //随机数字负载均衡
        //return new RandomLoadBalancer();
        //轮询负载均衡
        return new RoundRibbonLoadBalancer<>();
    }

    @Bean
    Router<InstanceMeta> router() {
        //return Router.Default;
        return new GrayRouter(consumerProperties.getGrayRatio());
    }

    // 需要在consumer实现静态注册中心的服务方法
    // 注解：定义初始化方法和销毁方法，这些方法在创建和销毁 bean 时会自动被调用。
    // 具体地说，通过使用 @Bean(initMethod = "start", destroyMethod = "stop") 注解，你可以告诉 Spring 容器，
    // 在创建 bean 时调用名为 "start" 的初始化方法（通常用于执行一些初始化逻辑，比如启动一些资源或连接），
    // 在销毁 bean 时调用名为 "stop" 的销毁方法（通常用于执行一些清理逻辑、释放资源的操作）。
    @Bean(initMethod = "start", destroyMethod = "stop")
    @ConditionalOnMissingBean
    public RegistryCenter consumer_rc() {
        //return new RegistryCenter.staticRegistryCenter(List.of(servers.split(",")));
        //将静态获取改为ZK获取
        //return new ZkRegistryCenter();
        return new GynRegistryCenter();
    }

    @Bean
    public Filter defaultFilter() {
        return new ParameterFilter();
    }

    /*@Bean
    public Filter defaultFilter() {
        return Filter.Default;
    }*/

    //缓存filter
    /*@Bean
    public Filter filter1(){
        return new CacheFilter();
    }*/

    //mock了就不cache,互斥了
    /*@Bean
    public Filter filter2(){
        return new MockFilter();
    }*/

    @Bean
    //@RefreshScope // context.refresh
    public RpcContext createContext(@Autowired Router router,
                                    @Autowired LoadBalancer loadBalancer,
                                    @Autowired List<Filter> filters) {
        RpcContext context = new RpcContext();
        context.setRouter(router);
        context.setLoadBalancer(loadBalancer);
        context.setFilters(filters);
        context.getParameters().put("app.id", appConfigProperties.getId());
        context.getParameters().put("app.namespace", appConfigProperties.getNamespace());
        context.getParameters().put("app.env", appConfigProperties.getEnv());
        context.setConsumerProperties(consumerProperties);
    return context;
    }
}
